테라폼으로 AWS 환경 구축하기 4장. 모듈을 이용하여 코드 다시 쓰기
2장에서 코드를 직접 써보고 3장에서 모듈 등 다른 기능에 대해서 알아보았습니다.
그런데 작성한 코드를 보면 중복된 내용이 들어가는 부분이 상당히 많습니다.
## public Seucurity Group resource "aws_security_group" "publicSG01" { name = "public-SG-01" description = "Allow all HTTP" vpc_id = aws_vpc.test.id ingress { cidr_blocks = [ "0.0.0.0/0" ] from_port = 80 protocol = "tcp" to_port = 80 } egress { cidr_blocks = [ "0.0.0.0/0" ] from_port = 0 protocol = "-1" to_port = 0 } } ## private Security Group resource "aws_security_group" "privateEC2SG01" { --- } resource "aws_security_group" "privateRDSSG01" { --- }
(변수 값만 조금씩 다른 리소스의 코드 블록이 많다..)
이렇게 코드를 작성하면 문제가 발견해도 찾지 못한다거나 코드가 길어져서 바로 이해하기 힘들어지는 등 관리하기가 힘들어집니다.
이전 글에서 알게 된 모듈이나 워크스페이스 등을 사용하여 이러한 문제를 해결해보도록 하겠습니다.
환경
전체적인 구성 자체는 저번 구성에서 Nat Gateway만을 추가하였습니다.
단 환경에 따라 변수를 다르게 조정하여 각 리소스의 스펙은 다르게 설정하겠습니다.
코드 작성
흐름
다음과 같은 흐름으로 작업을 진행합니다.
- 환경 나누기(dev/prd)
- 모듈, 환경 코드 작동 확인
- 모듈 및 환경 코드 작성
- 리소스 생성 확인 후 삭제
- prd 환경 코드 작성
환경 나누기
환경을 나누기 위해서는 크게 2가지 방법이 있습니다.
첫 번째는 환경 별로 다른 디렉터리를 만들고 파일을 관리하는 방법입니다.
- env - dev - main.tf - outputs.tf - variables.tf - terraform.tfvars - prd - main.tf - outputs.tf - variables.tf - terraform.tfvars - modules - iam - main.tf - variables.tf - outputs.tf - ec2 - main.tf - variables.tf - outputs.tf - ...
이렇게 관리하면 두 환경이 섞일 일이 없이 확실히 구분이 가능합니다. 또한 모듈만 완성 되면 다른 환경에서는 변수만 바꾸면 됩니다.
하지만 파일 수가 너무 많이 늘어나는 등 단점도 존재합니다.
이번 글에서는 prd 와 dev 라는 2개의 직접 환경 폴더를 나누어 관리하도록 하겠습니다.
다른 방법으로는 테라폼에서 제공하고 있는 워크스페이스기능을 이용하는 것 입니다. 워크스페이스를 이용하면 같은 코드라도 환경을 나누어 사용할 수 있습니다.
resource "foo" "bar" { name = "${terraform.workspace == "prd" ? "prd-resource" : "dev-resource"}" }
이와 같이 workspace 라는 파라미터로 적용할 값을 설정할 수 있습니다.
모듈, 환경 코드 작동 확인
서두에서 언급한대로 이전에 작성했던 코드를 보면 하나의 tf 파일에 모든 내용이 작성되어 있습니다.
이렇게 하나의 파일에 서비스에 관한 모든 리소스를 작성하면 코드가 너무 길어져 관리가 어렵게 됩니다. 또한 재사용도 할 수 없게 됩니다.
모듈화를 하면 이러한 문제점을 해결할 수 있습니다.
이전의 코드는 깃허브를 참고해주세요.
우선 디렉터리를 다음과 같이 환경(env)과 모듈(modules)로 나눕니다.
- env - dev - prd - modules - network - ec2 - ...
modules 아래에는 모듈화 할 서비스(EC2, RDS 등)나 기능(network 등) 단위로 디렉터리를 나눕니다. env 아래에는 사용할 환경 별로 디렉터리를 생성합니다.
그리고 아래와 같이 각 디렉터리 아래에 main.tf, variables.tf, outputs.tf, terraform.tfvars(환경 디렉터리만)
을 생성합니다.
- env - dev - main.tf - outputs.tf - variables.tf - terraform.tfvars - prd - ... - modules - network - main.tf - variables.tf - outputs.tf - ec2 - ... - ...
우선 잘 작동하는지 테스트하기 위해 간단하게 VPC를 작성하고 테스트 해보겠습니다.
모듈의 main.tf
에는 해당 모듈로 작성할 리소스(전체적인 틀)을 작성합니다.
# modules/network의 main.tf # VPC resource "aws_vpc" "vpc" { cidr_block = var.vpc_cidr tags = { "name" = "${var.env}-vpc" } } # Subnets resource "aws_subnet" "subnet" { vpc_id = aws_vpc.vpc.id cidr_block = var.subnet_cidr availability_zone = data.aws_availability_zones.available.names[0] tags = { "name" = "${var.env}-vpc" } } data "aws_availability_zones" "available" { state = "available" }
그리고 variables.tf
에는 main.tf
에서 이용하는 변수를 입력합니다.
이 때 모듈의 main.tf
에서 이용하는 변수는 해당 모듈 디렉터리의 variables.tf
와 환경 디렉터리의 variables.tf
양쪽에 정의되어 있어야합니다.
모듈 디렉터리의 variables.tf
는 빈 값으로 설정해도 괜찮습니다.
# modules/network/variables.tf variable "vpc_cidr" {} variable "env" {} variable "subnet_cidr" {} # env/dev/variables.tf variable "env" { description = "env name" default = "" } variable "vpc_cidr" { description = "vpc cidr" } variable "subnet_cidr" { description = "subnets cidr" }
여기까지 작성이 완료되었다면 dev
디렉터리 아래의 main.tf
을 다음과 같이 작성합니다.
위에서 모듈화 한 내용들을 main.tf
에서 불러와서 필요한 값만 지정해줍니다.
# env/dev/main.tf terraform { required_version = "~> 1.0.9" required_providers { aws = "~> 4.0" } } provider "aws" { region = "ap-northeast-1" } module "network" { source = "../../modules/network" vpc_cidr = var.vpc_cidr subnet_cidr = var.subnet_cidr env = var.env }
변수의 실제 값은 terraform.tfvars
에 정의합니다.
# env/dev/terraform.tfvars env = "dev" vpc_cidr = "202.2.0.0/16" subnet_cidr = "202.2.0.0/24"
여기까지 작성이 완료되었으면 dev
디렉터리에서 terraform init
으로 참조된 모듈을 정의한 후 terraform plan
으로 제대로 작동되는지 확인해봅시다.
모듈 및 환경 코드 작성
이후로 나머지 리소스(EC2, SecurityGroup, RDS ...)을 똑같이 모듈화 한 뒤 환경의 main.tf
에서 해당 모듈을 불러오고 설정하는 작업의 반복입니다.
기존의 코드를 어떤식으로 나누면 되는지 network 모듈을 예로 들어 설명하겠습니다.
network
모듈에는 VPC, Subnet, VPC Endpoint 등 네트워크와 관련된 리소스를 정의합니다.
이전의 코드를 확인해보면 서브넷이나 라우팅 테이블 연결 등 반복되는 부분이 많습니다.
# Subnets ## public subnet resource "aws_subnet" "publicSubnet1" { ... } ... resource "aws_subnet" "privateSubnet2" { ... } ### subnet associate resource "aws_route_table_association" "publicRTbAssociation01" { ... } ... resource "aws_route_table_association" "privateRTbAssociation04" { ... }
이렇게 반복되는 count
를 이용하여 간단하게 정의할 수 있습니다.
# modules/network/main.tf # 서브넷 생성의 반복 resource "aws_subnet" "public_subnet" { count = var.sub_count vpc_id = aws_vpc.vpc.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index) availability_zone = data.aws_availability_zones.available.names[count.index] tags = { "Name" = "${var.env}-public-subnet-${count.index + 1}" } } # 라우팅 테이블 연결의 반복 resource "aws_route_table_association" "public_rtb_association" { count = var.sub_count subnet_id = aws_subnet.public_subnet[count.index].id route_table_id = aws_route_table.public_rtb.id }
코드에 사용되는 sub_count
라는 변수는 위에서 설명한대로 variables.tf
에 정의할 필요가 있습니다.
이렇게 생성되는 리소스의 실제 스펙을 모듈에 정의해두면 환경 디렉터리의 main.tf
에서는 몇 개의 변수만 입력하면 몇 번이고 해당 모듈을 재사용 할 수 있습니다.
# env/dev/main.tf module "network" { source = "../../modules/network" vpc_cidr = var.vpc_cidr sub_count = var.resource_count env = var.env }
모듈을 정의한 후 terraform init
명령어로 설정 파일에 모듈을 등록합니다.
이 후 terraform plan -> terraform apply
로 리소스의 생성을 확인합니다.
이렇게 이전에 작성한 코드에서 반복의 정리 및 코드의 재사용 가능성을 고려하여 코드를 작성해나갑니다.
작성한 코드는 깃허브를 참고해주세요.
리소스 생성 확인 후 삭제
모든 코드를 작성한 후 terraform apply
를 실행하여 리소스가 전부 잘 생성되는지 확인하고 문제없이 생성되었다면 필요없는 비용이 발생하지 않도록 terraform destory
로 리소스를 삭제해줍니다.
prd 환경 코드 작성
dev 환경에서 모든 작업이 완료되었다면 env아래에 prd 디렉터리를 생성하여 dev 환경의 내용을 그대로 복사합니다. 그리고 terraform.tfvars
의 변수 값만 수정하면 간단하게 prd 환경의 리소스를 생성할 수 있습니다.
이렇게 간단하게 새로운 환경의 리소스를 작성할 수 있는게 모듈화의 장점입니다.
마치며
이번 글에서는 IAM 등의 리소스 작성은 하지 않았습니다. 필요에 따라 모듈을 생성해주세요.
모듈화를 잘 사용하면 재사용성을 높여 효율적인 리소스 관리가 가능해집니다.
저도 아직 많이 실력이 부족하여 코드가 아주 효율적이지는 못합니다. 모두 같이 노력하여 테라폼 마스터가 되도록 합시다!
긴 글 읽어주셔서 감사합니다.
오탈자 및 내용 피드백은 언제나 환영합니다. must01940 지메일로 연락 주시면 감사합니다!
본 블로그 게시글을 보시고 문의 사항이 있으신 분들은
클래스메소드코리아 (info@classmethod.kr)로 연락 주시면 빠른 시일 내 담당자가 회신 드릴 수 있도록 하겠습니다 !